﻿using System;
using System.IO;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Drawing.Text;

namespace BoYuan.Framework.Uitility
{
    /// <summary>
    /// 实现验证码的功能
    /// </summary>
    public class VerificationCode
    {
        #region 构造
        /// <summary>
        /// 初始化TestedCode类的新实例
        /// </summary>
        public VerificationCode()
        {

        }

        /// <summary>
        /// 用指定的大小初始化TestedCode类的新实例
        /// </summary>
        public VerificationCode(int Width, int Height)
        {
            _Width = Width;
            _Height = Height;
        }

        /// <summary>
        /// 用指定的大小和类型初始化TestedCode类的新实例
        /// </summary>
        public VerificationCode(int Width, int Height, CodeType CodeMode)
        {
            _Width = Width;
            _Height = Height;
            _CodeMode = CodeMode;
        }
        #endregion

        /// <summary>
        /// 生成输入文字汉字或是数字形式验证码
        /// <param name="isGetImgData">是否获取验证码图片字节</param>
        /// </summary>
        public string CreateCode(bool isGetImgData = false)
        {
            string sFont = CodeText().Replace(",", "");
            DrawingCode(sFont, isGetImgData);
            return sFont;
        }

        /// <summary>
        /// 运算验证码
        /// </summary>
        /// <returns></returns>
        public string ArithmeticCode()
        {
            int x, y, z;

            switch (Rnd.Next(1, 5))//随机数为1~4，不包括5
            {
                case 1://加法 +
                    x = Rnd.Next(1, 100);
                    y = Rnd.Next(1, 100);
                    DrawingCode(x + "+" + y + "=?");
                    return (x + y).ToString();
                case 2://减法 -
                    x = Rnd.Next(1, 90);
                    y = Rnd.Next(1, 9);
                    z = x + y;
                    DrawingCode(z + "-" + y + "=?");
                    return x.ToString();
                case 3://乘法 *×
                    x = Rnd.Next(1, 20);
                    y = Rnd.Next(1, 9);
                    DrawingCode(x + "×" + y + "=?");
                    return (x * y).ToString();
                case 4://除法 /÷
                    x = Rnd.Next(1, 20);
                    y = Rnd.Next(1, 9);
                    z = x * y;
                    DrawingCode(z + "÷" + y + "=?");
                    return x.ToString();
            }
            return "";
        }

        /// <summary>
        /// 随机算数或者是文字数字验证
        /// </summary>
        public string AllCode()
        {
            switch (Rnd.Next(1, 3))
            {
                case 1:
                    return CreateCode();
                default:
                    return ArithmeticCode();
            }
        }


        /// <summary>
        /// 画验证码
        /// </summary>
        /// <param name="sFont">验证字符串</param>
        /// <param name="isGetImgData">是否获取验证码图片字节</param>
        internal void DrawingCode(string sFont, bool isGetImgData = false)
        {
            Font font = GetFont();

            Bitmap bitmap = new Bitmap(_Width, _Height);
            Graphics g = Graphics.FromImage(bitmap);
            g.Clear(_BackgroundColor);

            LinearGradientBrush Bh = new LinearGradientBrush(new Point(0, bitmap.Height), new Point(bitmap.Width, bitmap.Height), AutoColor(_AutoColor)[Rnd.Next(AutoColor(_AutoColor).GetUpperBound(0) + 1)], AutoColor(_AutoColor)[Rnd.Next(AutoColor(_AutoColor).GetUpperBound(0) + 1)]);

            //画噪点
            if (_isPoint)
            {
                int c = (100 - _Definition) * 20;
                for (int i = 0; i < c; i++)
                {
                    Pen pen = new Pen(AutoColor(_AutoColor)[Rnd.Next(AutoColor(_AutoColor).GetUpperBound(0) + 1)], 0);
                    int x = Rnd.Next(bitmap.Width);
                    int y = Rnd.Next(bitmap.Height);
                    g.DrawRectangle(pen, x, y, 0.5F, 0.5F);
                    pen.Dispose();
                }
            }

            //画曲线
            if (_isCurve)
            {
                for (int i = 0; i < _CurveCount; i++)
                {
                    Pen pen = new Pen(AutoColor(_AutoColor)[Rnd.Next(AutoColor(_AutoColor).GetUpperBound(0) + 1)], _CurveWidth);
                    g.DrawBezier(pen, new Point(Rnd.Next(_Width), Rnd.Next(_Height)), new Point(Rnd.Next(_Width), Rnd.Next(_Height)), new Point(Rnd.Next(_Width), Rnd.Next(_Height)), new Point(Rnd.Next(_Width), Rnd.Next(_Height)));

                }
            }

            //画边框
            if (_isBorder)
            {
                g.DrawRectangle(new Pen(AutoColor(_AutoColor)[Rnd.Next(AutoColor(_AutoColor).GetUpperBound(0) + 1)]), new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1));
            }

            Brush brush = null;
            if (_FontColor == Color.Empty)
            {
                brush = Bh;
            }
            else
            {
                Pen pn = new Pen(_FontColor);
                brush = pn.Brush;
                pn.Dispose();
            }

            g.RotateTransform(Rnd.Next(_Rotation - (_Rotation * 2), _Rotation));
            g.DrawString(sFont, font, brush, Rnd.Next(Math.Abs(_Width - Rectangular(sFont).Width)), Rnd.Next(Math.Abs(_Height - Rectangular(sFont).Height)));
            brush.Dispose();
            g.Dispose();
            Bh.Dispose();
            font.Dispose();
            WriteStream(bitmap, isGetImgData);
        }

        /// <summary>
        /// 颜色数组
        /// </summary>
        /// <param name="HtmlColor"></param>
        /// <returns></returns>
        internal Color[] AutoColor(Color[] HtmlColor)
        {

            if (HtmlColor == null)
            {
                Fc = new Color[37]
            {
                ColorTranslator.FromHtml("#DCDCDC"),
                ColorTranslator.FromHtml("#668B8B"),
                ColorTranslator.FromHtml("#FFDEAD"),
                ColorTranslator.FromHtml("#660099"),
                ColorTranslator.FromHtml("#458B74"),
                ColorTranslator.FromHtml("#ff00ff"),
                ColorTranslator.FromHtml("#708090"),
                ColorTranslator.FromHtml("#9999ff"),
                ColorTranslator.FromHtml("#90EE90"),
                ColorTranslator.FromHtml("#00FF00"),
                ColorTranslator.FromHtml("#1E90FF"),
                ColorTranslator.FromHtml("#C0FF3E"),
                ColorTranslator.FromHtml("#E0FFFF"),
                ColorTranslator.FromHtml("#FFF68F"),
                ColorTranslator.FromHtml("#8B814C"),
                ColorTranslator.FromHtml("#8B6914"),
                ColorTranslator.FromHtml("#8B658B"),
                ColorTranslator.FromHtml("#CD5C5C"),
                ColorTranslator.FromHtml("#FF8247"),
                ColorTranslator.FromHtml("#FF1493"),
                ColorTranslator.FromHtml("#EE2C2C"),
                ColorTranslator.FromHtml("#FF00FF"),
                ColorTranslator.FromHtml("#8A2BE2"),
                ColorTranslator.FromHtml("#FFFACD"),
                ColorTranslator.FromHtml("#CD1076"),
                ColorTranslator.FromHtml("#E0EEE0"),
                ColorTranslator.FromHtml("#0000FF"),
                ColorTranslator.FromHtml("#B0E2FF"),
                ColorTranslator.FromHtml("#0066cc"),
                ColorTranslator.FromHtml("#ff9900"),
                ColorTranslator.FromHtml("#660099"),
                ColorTranslator.FromHtml("#006600"),
                ColorTranslator.FromHtml("#ff00ff"),
                ColorTranslator.FromHtml("#ffff00"),
                ColorTranslator.FromHtml("#9999ff"),
                ColorTranslator.FromHtml("#666600"),
                ColorTranslator.FromHtml("#336633")
            };
            }
            else
            {
                Fc = HtmlColor;
            }
            return Fc;
        }

        /// <summary>
        /// 创建验证码文本
        /// </summary>
        /// <returns></returns>
        internal string CodeText()
        {
            Array ar = AnyMode();
            string[] Any = (string[])ar;
            string CodeText = "";
            for (int i = 0; i < this._Median; i++)
            {
                CodeText += Any.GetValue(Rnd.Next(0, ar.Length)) + ",";
            }

            return CodeText;
        }


        /// <summary>
        /// 字体尺寸
        /// </summary>
        /// <param name="sFont"></param>
        /// <returns></returns>
        internal Size Rectangular(string sFont)
        {
            Font font = new Font(_FontName, _FontSize, _FontStyle, GraphicsUnit.Pixel);
            Graphics Measure = Graphics.FromImage(new Bitmap(1, 1));
            Measure.PageUnit = GraphicsUnit.Pixel;
            return Measure.MeasureString(sFont, font).ToSize();
        }

        /// <summary>
        /// 随机获取字体
        /// </summary>
        /// <returns></returns>
        internal Font GetFont()
        {
            InstalledFontCollection fonts = new InstalledFontCollection();//系统字体库
            FontFamily[] ff = fonts.Families;
            int ffNum = Rnd.Next(0, ff.Length);//使用随机字体库

            try
            {
                Font font = new Font(ff[ffNum], _FontSize, _FontStyle, GraphicsUnit.Pixel);
                return font;
            }
            catch//有些字体不支持斜体等格式
            {
                Font font = new Font(ff[ffNum], _FontSize);
                return font;
            }
        }

        /// <summary>
        /// 创建验证码字库
        /// </summary>
        /// <returns></returns>
        internal Array AnyMode()
        {
            string[] D;
            string[] GlobalArray = new string[61] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                                                    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                                                    "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
                                                    "U", "V", "W", "X", "Y", "Z",  "",  "",  "",  "",
                                                    "",  "",  "",  "",  "",  "",   "",  "",  "",  "",
                                                    "",  "",  "",  "",  "",   "",  "",  "",  "",  "",
                                                    ""};
            //再添加25个随机汉字,加入到GlobalArray中
            System.Text.Encoding gb = System.Text.Encoding.GetEncoding("gb2312");
            object[] str = CreateRegionCode(25);
            for (int i = 0; i < str.Length; i++)
            {
                GlobalArray[i + 36] = gb.GetString((byte[])Convert.ChangeType(str[i], typeof(byte[])));
            }


            switch (this._CodeMode)
            {
                case CodeType.D:
                    D = new string[10];
                    Array.Copy(GlobalArray, D, 10);
                    return D;
                case CodeType.L:
                    D = new string[26];
                    Array.Copy(GlobalArray, 10, D, 0, 26);
                    return D;
                case CodeType.C:
                    D = new string[25];
                    Array.Copy(GlobalArray, 36, D, 0, 25);
                    return D;
                case CodeType.D_C:
                    D = new string[35];
                    Array.Copy(GlobalArray, D, 10);
                    Array.Copy(GlobalArray, 36, D, 10, 25);
                    return D;
                case CodeType.D_L:
                    D = new string[36];
                    Array.Copy(GlobalArray, D, 36);
                    return D;
                case CodeType.L_C:
                    D = new string[51];
                    Array.Copy(GlobalArray, 10, D, 0, 61);
                    return D;
                case CodeType.D_L_C:
                    return GlobalArray;
                default:
                    return GlobalArray;
            }
        }


        /// <summary>
        /// 输出验证码图片流
        /// </summary>
        /// <param name="bitmap"></param>
        /// <param name="isGetImgData">是否获取验证码图片字节</param>
        internal void WriteStream(Image bitmap, bool isGetImgData = false)
        {
            ImageCodecInfo myImageCodecInfo;
            System.Drawing.Imaging.Encoder myEncoder;
            EncoderParameter myEncoderParameter;
            EncoderParameters myEncoderParameters;

            myImageCodecInfo = ImageCodecInfo.GetImageEncoders()[0];
            myEncoder = System.Drawing.Imaging.Encoder.Quality;
            myEncoderParameters = new EncoderParameters(1);
            myEncoderParameter = new EncoderParameter(myEncoder, 100L);
            myEncoderParameters.Param[0] = myEncoderParameter;

            MemoryStream Ms = new MemoryStream();
            bitmap.Save(Ms, myImageCodecInfo, myEncoderParameters);
            bitmap.Dispose();
            myEncoderParameter.Dispose();
            myEncoderParameters.Dispose();

            byte[] mydata = Ms.ToArray();
            int Length = Convert.ToInt32(Ms.Length);
            Ms.Close();
            Ms.Dispose();
            if (isGetImgData)
            {
                ImgData = mydata;
            }
            else
            {
                HttpContext.Current.Response.OutputStream.Write(mydata, 0, Length);
            }
        }

        #region  获取随机汉字编码
        /* 
        此函数在汉字编码范围内随机创建含两个元素的十六进制字节数组，每个字节数组代表一个汉字，并将 
        四个字节数组存储在object数组中。 
        参数：strlength，代表需要产生的汉字个数 
        */
        /// <summary>
        /// 获取随机汉字编码
        /// </summary>
        /// <param name="strlength"></param>
        /// <returns></returns>
        internal object[] CreateRegionCode(int strlength)
        {
            //定义一个字符串数组储存汉字编码的组成元素 
            string[] rBase = new String[16] { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };


            //定义一个object数组用来 
            object[] bytes = new object[strlength];

            /**/
            /*每循环一次产生一个含两个元素的十六进制字节数组，并将其放入bject数组中 
            每个汉字有四个区位码组成 
            区位码第1位和区位码第2位作为字节数组第一个元素 
            区位码第3位和区位码第4位作为字节数组第二个元素 
            */
            for (int i = 0; i < strlength; i++)
            {
                //区位码第1位 
                int r1 = Rnd.Next(11, 14);
                string str_r1 = rBase[r1].Trim();

                //区位码第2位 
                Rnd = new Random(r1 * unchecked((int)DateTime.Now.Ticks) + i);//更换随机数发生器的 

                //种子避免产生重复值 
                int r2;
                if (r1 == 13)
                {
                    r2 = Rnd.Next(0, 7);
                }
                else
                {
                    r2 = Rnd.Next(0, 16);
                }
                string str_r2 = rBase[r2].Trim();

                //区位码第3位                
                int r3 = Rnd.Next(10, 16);
                string str_r3 = rBase[r3].Trim();

                //区位码第4位              
                int r4;
                if (r3 == 10)
                {
                    r4 = Rnd.Next(1, 16);
                }
                else if (r3 == 15)
                {
                    r4 = Rnd.Next(0, 15);
                }
                else
                {
                    r4 = Rnd.Next(0, 16);
                }
                string str_r4 = rBase[r4].Trim();

                //定义两个字节变量存储产生的随机汉字区位码 
                byte byte1 = Convert.ToByte(str_r1 + str_r2, 16);
                byte byte2 = Convert.ToByte(str_r3 + str_r4, 16);
                //将两个字节变量存储在字节数组中 
                byte[] str_r = new byte[] { byte1, byte2 };

                //将产生的一个汉字的字节数组放入object数组中 
                bytes.SetValue(str_r, i);
            }

            return bytes;

        }
        #endregion

        #region  属性 验证码的类型
        /// <summary>
        /// 验证码的类型
        /// </summary>
        public enum CodeType
        {
            /// <summary>
            /// 验证码为(0-9)的数字
            /// </summary>
            D,
            /// <summary>
            /// 验证码为(a-z)的字母
            /// </summary>
            L,
            /// <summary>
            /// 验证码为汉字(五笔的一级简码)
            /// </summary>
            C,
            /// <summary>
            /// 验证码为(0-9)的数字加(a-z)的字母
            /// </summary>
            D_L,
            /// <summary>
            ///验证码为(0-9)的数字加汉字(五笔的一级简码)
            /// </summary>
            D_C,
            /// <summary>
            ///验证码为(a-z)的字母加汉字(五笔的一级简码)
            /// </summary>
            L_C,
            /// <summary>
            /// 验证码为(0-9)的数字加(a-z)的字母加汉字(五笔的一级简码)
            /// </summary>
            D_L_C
        }

        private int _Median = 6;
        private Random Rnd = new Random();
        private Color[] Fc = null;
        private CodeType _CodeMode = CodeType.D_L;
        private int _Width = 150;
        private int _Height = 50;
        private int _FontSize = 22;
        private string _FontName = "Arial, Helvetica, sans-serif";
        private FontStyle _FontStyle = FontStyle.Italic;
        private Color _FontColor = Color.Empty;
        private int _Definition = 75;
        private int _Rotation = 5;
        private bool _isBorder = true;
        private Color _isBorderColor = ColorTranslator.FromHtml("#336699");
        private Color _BackgroundColor = ColorTranslator.FromHtml("#ffffff");
        private Color[] _AutoColor = null;
        private bool _isPoint = true;
        private bool _isCurve = true;
        private int _CurveCount = 2;
        private int _CurveWidth = 3;

        /// <summary>
        /// 设置验证码的位数
        /// </summary>
        public int Median
        {
            get { return _Median; }
            set { _Median = value; }
        }

        /// <summary>
        /// 设置验证码的模式
        /// </summary>
        public CodeType CodeMode
        {
            get { return _CodeMode; }
            set { _CodeMode = value; }
        }

        /// <summary>
        /// 设置验证码的宽度
        /// </summary>
        public int Width
        {
            get { return _Width; }
            set { _Width = value; }
        }

        /// <summary>
        /// 设置验证码的高度
        /// </summary>
        public int Height
        {
            get { return _Height; }
            set { _Height = value; }
        }

        /// <summary>
        /// 设置验证码字体大小
        /// </summary>
        public int FontSize
        {
            get { return _FontSize; }
            set { _FontSize = value; }
        }

        /// <summary>
        /// 设置验证码字体
        /// </summary>
        public string FontName
        {
            get { return _FontName; }
            set { _FontName = value; }
        }

        /// <summary>
        /// 设置验证码字体样式
        /// </summary>
        public FontStyle Style
        {
            get { return _FontStyle; }
            set { _FontStyle = value; }
        }

        /// <summary>
        /// 设置验证码字体颜色
        /// </summary>
        public Color FontColor
        {
            get { return _FontColor; }
            set { _FontColor = value; }
        }

        /// <summary>
        /// 设置验证码的清晰度
        /// </summary>
        public int Definition
        {
            get { return _Definition; }
            set { _Definition = value; }
        }

        /// <summary>
        /// 设置验证码中字体旋转的角度
        /// </summary>
        public int Rotation
        {
            get { return _Rotation; }
            set { _Rotation = value; }
        }

        /// <summary>
        /// 指示此验证码是否有边框
        /// </summary>
        public bool isBorder
        {
            get { return _isBorder; }
            set { _isBorder = value; }
        }

        /// <summary>
        /// 设置验证码边框颜色
        /// </summary>
        public Color isBorderColor
        {
            get { return _isBorderColor; }
            set { _isBorderColor = value; }
        }

        /// <summary>
        /// 设置验证码背景颜色
        /// </summary>
        public Color BackgroundColor
        {
            get { return _BackgroundColor; }
            set { _BackgroundColor = value; }
        }

        /// <summary>
        /// 设置自动颜色
        /// </summary>
        public Color[] setAutoColor
        {
            get { return _AutoColor; }
            set { _AutoColor = value; }
        }

        /// <summary>
        /// 指示此验证码的背景是否显示杂点
        /// </summary>
        public bool isPoint
        {
            get { return _isPoint; }
            set { _isPoint = value; }
        }

        /// <summary>
        /// 指示此验证码的背景是否显示曲线
        /// </summary>
        public bool isCurve
        {
            get { return _isCurve; }
            set { _isCurve = value; }
        }

        /// <summary>
        /// 指示此验证码的背景显示曲线的数量
        /// </summary>
        public int CurveCount
        {
            get { return _CurveCount; }
            set { _CurveCount = value; }
        }

        /// <summary>
        /// 指示此验证码的背景显示曲线的宽度
        /// </summary>
        public int CurveWidth
        {
            get { return _CurveWidth; }
            set { _CurveWidth = value; }
        }

        /// <summary>
        /// 生成的验证码图片字节
        /// </summary>
        public byte[] ImgData { get; set; }

        #endregion
    }
}
/*
 //mvc调用示例
 
        [AllowAnonymous]
        public ActionResult GetValidateCode()
        {
            VerificationCode vCode = new VerificationCode(91,45);
            vCode.CodeMode = VerificationCode.CodeType.D;
            vCode.Median = 4;
            vCode.FontSize = 34;
            string code = vCode.CreateCode(true);
            Session["ValidateCode"] = code;
            byte[] bytes = vCode.ImgData;
            return File(bytes, @"image/jpeg");
        }
 */